# 7. 数组

# 数组

数组是同一种数据类型元素的集合。 在Go语言中,数组从声明时就确定,使用时可以修改数组成员,但是数组大小不可变化

数组可以存放多个同一类型数据。数组也是一种数据类型,在 Go 中,数组是值类型

基本语法:

// 定义一个长度为3元素类型为int的数组a
var a [3]int

# 数组定义

var 数组变量名 [元素数量]T

比如:var a [5]int, 数组的长度必须是常量,并且长度是数组类型的一部分。一旦定义,长度不能变。 [5]int[10]int是不同的类型。

var a [3]int
var b [4]int
a = b //不可以这样做,因为此时a和b是不同的类型

数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1,访问越界(下标在合法范围之外),则触发访问越界,会panic。

# 数组的初始化

数组的初始化也有很多方式。

# 初始化时指定数组大小

初始化数组时可以使用初始化列表来设置数组元素的值。

func main() {
	var testArray [3]int                        //数组会初始化为int类型的零值
	var numArray = [3]int{1, 2}                 //使用指定的初始值完成初始化
	var cityArray = [3]string{"北京", "上海", "深圳"} //使用指定的初始值完成初始化
	fmt.Println(testArray)                      
	fmt.Println(numArray)                       
	fmt.Println(cityArray)                      
}

结果:
[0 0 0]
[1 2 0]
[北京 上海 深圳]

# 初始化时让编译器判断数据大小(不指定,不支持多维数组)

按照上面的方法每次都要确保提供的初始值和数组长度一致,一般情况下我们可以让编译器根据初始值的个数自行推断数组的长度,例如:

func main() {
	var testArray [3]int
	var numArray = [...]int{1, 2}
	var cityArray = [...]string{"北京", "上海", "深圳"}
	fmt.Println(testArray)                          //[0 0 0]
	fmt.Println(numArray)                           //[1 2]
	fmt.Printf("type of numArray:%T\n", numArray)   //type of numArray:[2]int
	fmt.Println(cityArray)                          //[北京 上海 深圳]
	fmt.Printf("type of cityArray:%T\n", cityArray) //type of cityArray:[3]string
}

结果:
[0 0 0]
[1 2]
type of numArray:[2]int
[北京 上海 深圳]
type of cityArray:[3]string

# 指定索引值的方式来初始化数组

我们还可以使用指定索引值的方式来初始化数组

func main() {
	a := [...]int{1: 1, 3: 5}
	fmt.Println(a)                  // [0 1 0 5]
	fmt.Printf("type of a:%T\n", a) //type of a:[4]int
}

结果:
[0 1 0 5]
type of a:[4]int

为什么会是[0 1 0 5],而不是[1 5],请详细看一下索引值

# 数组的遍历

遍历数组a有以下两种方法:

func main() {
	var a = [...]string{"北京", "上海", "深圳"}
	// 方法1:for循环遍历
	for i := 0; i < len(a); i++ {
		fmt.Println(a[i])
	}

	// 方法2:for range遍历
	for index, value := range a {
		fmt.Println(index, value)
	}
    
    // 方法2:for range遍历 - 不要索引值,就只要内容值
	for _, value := range a {
		fmt.Println(value)
	}
}

结果:
  第一种:
	北京
	上海
	深圳
  第二种:
	0 北京
	1 上海
	2 深圳
	北京
	上海
	深圳

# 多维数组

Go语言是支持多维数组的,我们这里以二维数组为例(数组中又嵌套数组)

# 二维数组的定义

func main() {
	a := [3][2]string{
		{"北京", "上海"},
		{"广州", "深圳"},
		{"成都", "重庆"},
	}
	fmt.Println(a)
	fmt.Println(a[2][1]) //支持索引取值
}

结果:
[[北京 上海] [广州 深圳] [成都 重庆]]
重庆

# 二维数组的遍历

func main() {
	a := [3][2]string{
		{"北京", "上海"},
		{"广州", "深圳"},
		{"成都", "重庆"},
	}
	for _, v1 := range a {
		for _, v2 := range v1 {
			fmt.Printf("%s\t", v2)
		}
		fmt.Println()
	}
}

结果:
北京	上海	
广州	深圳	
成都	重庆

# 多维数组只支持第一层使用[...]

注意: 多维数组只有第一层可以使用...来让编译器推导数组长度。例如:

//支持的写法
a := [...][2]string{
	{"北京", "上海"},
	{"广州", "深圳"},
	{"成都", "重庆"},
}
//不支持多维数组的内层使用...
b := [3][...]string{
	{"北京", "上海"},
	{"广州", "深圳"},
	{"成都", "重庆"},
}

# 数组是值类型

数组是值类型,赋值和传参会复制整个数组。因此改变副本的值,不会改变本身的值。

func modifyArray(x [3]int) {
	x[0] = 100
}

func modifyArray2(x [3][2]int) {
	x[2][0] = 100
}
func main() {
	a := [3]int{10, 20, 30}
	modifyArray(a) //在modify中修改的是a的副本x
	fmt.Println(a)
	b := [3][2]int{
		{1, 1},
		{1, 1},
		{1, 1},
	}
	modifyArray2(b) //在modify中修改的是b的副本x
	fmt.Println(b) 
}

结果:
[10 20 30]
[[1 1] [1 1] [1 1]]

# 数组注意事项

  1. 数组支持 “==“、”!=” 操作符,因为内存总是被初始化过的。

  2. [n]*T表示指针数组,*[n]T表示数组指针 。

  3. 数组是多个相同类型数据的组合,一个数组一旦声明/定义了,其长度是固定的, 不能动态变化

  4. var arr []int 这时 arr 就是一个 slice 切片,切片后面专门讲解,不急哈.

  5. 数组中的元素可以是任何数据类型,包括值类型和引用类型,但是不能混用。

  6. 数组创建后,如果没有赋值,有默认值(零值)

    1. 数值类型数组:默认值为 0

    2. 字符串数组: 默认值为 ""

    3. bool 数组: 默认值为 false

  7. 使用数组的步骤 1. 声明数组并开辟空间 2 给数组各个元素赋值(默认零值) 3 使用数组

  8. 数组的下标是从 0 开始的

  9. 数组下标必须在指定范围内使用,否则报 panic:数组越界,比如

    var arr [5]int 则有效下标为 0-4
    
  10. Go 的数组属值类型, 在默认情况下是值传递, 因此会进行值拷贝。数组间不会相互影响

  11. 如想在其它函数中,去修改原来的数组,可以使用引用传递(指针方式)

  12. 长度是数组类型的一部分,在传递函数参数时 需要考虑数组的长度

# 数组的应用案例

  1. 创建一个 byte 类型的 26 个元素的数组,分别 放置'A'-'Z‘。使用 for 循环访问所有元素并打印出来。提示:字符数据运算 'A'+1 -> 'B'image-20201118195933105
  2. 请求出一个数组的最大值,并得到对应的下标image-20201118195925082
  3. 请求出一个数组的和和平均值。for-rangeimage-20201118195916457
  4. 要求:随机生成五个数,并将其反转打印 , 复杂应用image-20201118195902244